// Copyright 2023 Huawei Cloud Computing Technology Co., Ltd.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

import Util from "./Util";
import work from 'webworkify-webpack';
import PROTOCOL_CONFIG from  './config/protocolConfig';
import {CAMERA_COMMON_MODE_MAP} from './config/commonConfig';

const OPT_CAMERA_FRAME = PROTOCOL_CONFIG.CAMERA_MESSAGE_TYPE.OPT_CAMERA_FRAME;

export default class CameraPlayer {
    constructor(options) {
        this.options = {...options};
        this.sendOutBufHandler = options.sendOutBufHandler;
        this.util = new Util();
        if (this.options.videoOptions) {
            this.video = document.createElement('video');
            this.canvas = document.createElement('canvas');
            this.initWebGl();
            this.videoStream = null;
            this.animationId = null;
            this.stopVideo = false;
            this.videoEncoderWorker = work(require.resolve('./worker/VideoEncoder.js'));
            this.videoEncoderWorker.addEventListener('message', this.videoMessageHandler.bind(this));
            this.initMediaDevices(this.options.videoOptions);
        }
    }

    initMediaDevices(videoOptions) {
        if (navigator.mediaDevices === undefined) {
            navigator.mediaDevices = {};
        }

        if (navigator.mediaDevices.getUserMedia === undefined) {
            navigator.mediaDevices.getUserMedia = (constraints) => {
        
            let getUserMedia = navigator.webkitGetUserMedia || navigator.mozGetUserMedia || navigator.getUserMedia || navigator.msGetUserMedia;

            if (!getUserMedia) {
                return Promise.reject(new Error('getUserMedia is not implemented in this browser'));
            }

            return new Promise((resolve, reject) => {
                getUserMedia.call(navigator, constraints, resolve, reject);
            });
            }
        }
        
        navigator.mediaDevices.getUserMedia({ video: videoOptions, audio: false })
        .then((stream) => {
            // 视频轨道
            this.videoStream = stream.getVideoTracks()[0];
            // 摄像头打开
            if (this.videoStream) {
                this.videoStreamHandle();
            }
        })
        .catch((err) => {
            console.log(err.name + ": " + err.message);
        });
    }

    render() {
        this.updateTexture();
        // 创建一个Uint8Array数组，用于存储像素数据
        const pixels = new Uint8Array(this.canvas.width * this.canvas.height * 4);
        // 读取像素数据
        this.gl.readPixels(0, 0, this.canvas.width, this.canvas.height, this.gl.RGBA, this.gl.UNSIGNED_BYTE, pixels);
        const imageData = this.getImageData(pixels, this.canvas.width, this.canvas.height);

        this.videoEncoderWorker.postMessage({
            type: 'encode',
            data: imageData
        });
    }

    getImageData(data, width, height) {
        const canvas = document.createElement('canvas');
        const ctx = canvas.getContext('2d');
        const imageData = ctx.createImageData(width, height);
        imageData.data.set(data);
        return imageData;
    }

    videoStreamHandle() {
        const newVideoStream = new MediaStream();
        newVideoStream.addTrack(this.videoStream);
        this.video.srcObject = newVideoStream;
        this.video.addEventListener('loadedmetadata', () => {
            this.canvas.width = this.options.encodeWidth;
            this.canvas.height = this.options.encodeHeight;
            this.videoEncoderWorker.postMessage({
                type: 'initEncode',
                width: this.canvas.width,
                height: this.canvas.height,
                x264WasmPath: this.options.x264WasmPath
            });

            // 指定宽高
            this.gl.viewport(0, 0, this.canvas.width, this.canvas.height);
            // 绑定纹理对象
            this.gl.bindTexture(this.gl.TEXTURE_2D, this.texture);
            // 将视频帧绘制到纹理对象
            this.gl.texImage2D(this.gl.TEXTURE_2D, 0, this.gl.RGBA, this.gl.RGBA, this.gl.UNSIGNED_BYTE, this.video);
            // 绘制纹理
            this.gl.drawArrays(this.gl.TRIANGLE_STRIP, 0, 4);
            
        });

        this.video.play();
    }

    videoMessageHandler(evt) {
        const message = evt.data;
        if (!Object.prototype.hasOwnProperty.call(message, 'type')) {
            return;
        }

        switch (message.type) {
            case 'startEncode': {
                this.animationId = requestAnimationFrame(this.render.bind(this));
                break;
            }
            case 'encodeResult': {
                this.sendOutBufHandler('CAMERA', OPT_CAMERA_FRAME, message.data);
                this.animationId = requestAnimationFrame(this.render.bind(this));
                if (this.stopVideo) {
                    cancelAnimationFrame(this.animationId);
                }
                break;
            }
            case 'setRotateData': {
                const rotateData = message.data;
                this.gl.uniformMatrix4fv(this.rotationMatrixLocation, false, rotateData);
                this.gl.uniform1i(this.textureLocation, 0);
                // 绘制纹理
                this.gl.drawArrays(this.gl.TRIANGLE_STRIP, 0, 4);
                break;
            }
            default:
                break;
        }
    }

    stopCameraVideo() {
        this.stopVideo = true;
        if (this.video.srcObject) {
            this.video.srcObject.getTracks().forEach(track => track.stop());
        }
        this.video.srcObject = null;
        if (this.videoStream) {
            this.videoStream.stop();
        }
    }

    initWebGl() {
        // 获取webgl上下文
        this.gl = this.canvas.getContext('webgl');
        // 创建顶点着色器
        const vertexShaderSource = `
        attribute vec4 a_position;
        attribute vec2 a_texCoord;
        uniform mat4 u_rotationMatrix;
        varying vec2 v_texCoord;
        void main() {
            gl_Position = u_rotationMatrix * a_position;
            v_texCoord = a_texCoord;
        }`;
        const vertexShader = this.gl.createShader(this.gl.VERTEX_SHADER);
        this.gl.shaderSource(vertexShader, vertexShaderSource);
        this.gl.compileShader(vertexShader);

        // 创建片元着色器
        const fragmentShaderSource = `
        precision mediump float;
        uniform sampler2D u_texture;
        varying vec2 v_texCoord;
        void main() {
            gl_FragColor = texture2D(u_texture, v_texCoord);
        }`;
        const fragmentShader = this.gl.createShader(this.gl.FRAGMENT_SHADER);
        this.gl.shaderSource(fragmentShader, fragmentShaderSource);
        this.gl.compileShader(fragmentShader);

        // 创建着色器程序
        const program = this.gl.createProgram();
        this.gl.attachShader(program, vertexShader);
        this.gl.attachShader(program, fragmentShader);
        this.gl.linkProgram(program);
        this.gl.useProgram(program);

        this.rotationMatrixLocation = this.gl.getUniformLocation(program, 'u_rotationMatrix');
        this.textureLocation = this.gl.getUniformLocation(program, 'u_texture');

        // 创建顶点缓冲区
        const positionBuffer = this.gl.createBuffer();
        this.gl.bindBuffer(this.gl.ARRAY_BUFFER, positionBuffer);
        this.gl.bufferData(this.gl.ARRAY_BUFFER, new Float32Array([
            -1, -1,
            1, -1,
            -1, 1,
            1, 1
        ]), this.gl.STATIC_DRAW);

        // 创建纹理缓冲区
        const texCoordBuffer = this.gl.createBuffer();
        this.gl.bindBuffer(this.gl.ARRAY_BUFFER, texCoordBuffer);
        this.gl.bufferData(this.gl.ARRAY_BUFFER, new Float32Array([
            0, 0,
            1, 0,
            0, 1,
            1, 1,
        ]), this.gl.STATIC_DRAW);

        // 设置顶点属性指针
        const positionLocation = this.gl.getAttribLocation(program, 'a_position');
        this.gl.enableVertexAttribArray(positionLocation);
        this.gl.bindBuffer(this.gl.ARRAY_BUFFER, positionBuffer);
        this.gl.vertexAttribPointer(positionLocation, 2, this.gl.FLOAT, false, 0, 0);

        // 设置纹理属性指针
        const texCoordLocation = this.gl.getAttribLocation(program, 'a_texCoord');
        this.gl.enableVertexAttribArray(texCoordLocation);
        this.gl.bindBuffer(this.gl.ARRAY_BUFFER, texCoordBuffer);
        this.gl.vertexAttribPointer(texCoordLocation, 2, this.gl.FLOAT, false, 0, 0);

        // 创建纹理对象
        this.texture = this.gl.createTexture();
        this.gl.bindTexture(this.gl.TEXTURE_2D, this.texture);

        // 设置纹理参数
        this.gl.texParameteri(this.gl.TEXTURE_2D, this.gl.TEXTURE_WRAP_S, this.gl.CLAMP_TO_EDGE);
        this.gl.texParameteri(this.gl.TEXTURE_2D, this.gl.TEXTURE_WRAP_T, this.gl.CLAMP_TO_EDGE);
        this.gl.texParameteri(this.gl.TEXTURE_2D, this.gl.TEXTURE_MIN_FILTER, this.gl.LINEAR);
        this.gl.texParameteri(this.gl.TEXTURE_2D, this.gl.TEXTURE_MAG_FILTER, this.gl.LINEAR);
    }

    // 每帧更新纹理
    updateTexture() {
        let rotateAngles = -90 * Math.PI / 180;
        if (this.options.devId === CAMERA_COMMON_MODE_MAP.USER) {
            rotateAngles = Math.PI / 2;
        } else {
            rotateAngles = -90 * Math.PI / 180;
        }
        // 绑定纹理对象
        this.gl.bindTexture(this.gl.TEXTURE_2D, this.texture);
        // 将视频帧绘制到纹理对象
        this.gl.texImage2D(this.gl.TEXTURE_2D, 0, this.gl.RGBA, this.gl.RGBA, this.gl.UNSIGNED_BYTE, this.video);
        
        this.videoEncoderWorker.postMessage({
            type: 'rotateTexture',
            rotateAngles
        });
    }
}